from sys import argv
from OpenGL.GL import *
from OpenGL.GLU import *
from OpenGL.GLUT import *

class MyPyOpenGLTest:
    # 初始化OpenGL环境，指定显示模式以及用于绘图的函数
    def __init__(self, width=640, height=480,
                 title='PyOpenGL-Bresenham画直线'.encode('gbk')):
        glutInit(argv)
        # 使用RGBA颜色、双缓冲
        glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE | GLUT_DEPTH)
        # 设置图形窗口初始大小和位置，然后创建图形窗口，顺序不能变
        glutInitWindowSize(width, height)
        glutInitWindowPosition(400, 200)
        glutCreateWindow(title)
        # 指定绘制图形时执行的方法
        glutDisplayFunc(self.draw)
        # 指定调整窗口大小时执行的方法
        glutReshapeFunc(self.reshape)
        # 指定系统空闲时执行的方法
        # glutIdleFunc(self.draw)
        # 进一步初始化，完成个性化设置
        self.InitGL(width, height)

    # 根据特定的需要，进一步完成OpenGL的初始化
    def InitGL(self, width, height):
        # 初始化窗口背景为白色
        glClearColor(1.0, 1.0, 1.0, 1.0)
        # 切换到透视投影矩阵
        glMatrixMode(GL_PROJECTION)
        # 加载单位矩阵，重置
        glLoadIdentity()
        gluOrtho2D(0, width, 0, height)
        # 切换到模型观察矩阵
        glMatrixMode(GL_MODELVIEW)
        glLoadIdentity()

    # 调整图形窗口大小时执行的方法
    def reshape(self, w, h):
        if h == 0:
            h = 1
        ratio = w / h
        # 设置视口/视区的左下角x、y坐标以及窗口宽度和高度
        # 视口是最终用来显示图形的区域
        glViewport(0, 0, w, h)

        # 切换到透视投影矩阵
        glMatrixMode(GL_PROJECTION)
        glLoadIdentity()
        # 设置世界窗口的左下角x坐标、右上角x坐标、左下角y坐标、右上角y坐标
        # 这是需要显示到视口的图形区域
        gluOrtho2D(0, w, 0, h)
        # 修改为下面的大小可以看到直线段被放大后像素离散的状态
        # gluOrtho2D(0, 100, 0, 100)
        # 切换到模型观察矩阵
        glMatrixMode(GL_MODELVIEW)
        glLoadIdentity()

    def draw_line_Bresenham(self, x1, y1, xn, yn):
        dx = xn -x1
        dy = yn - y1
        if dx == 0:
            # 垂直线段
            glBegin(GL_POINTS)
            for y in range(*sorted((y1, yn))):
                glVertex2f(x1, y)
            glEnd()
        elif dy == 0:
            # 水平线段
            # 垂直线段
            glBegin(GL_POINTS)
            for x in range(*sorted((x1, xn))):
                glVertex2f(x1, x)
            glEnd()
        else:
            k = dy / dx
            if 0 <= k <= 1:
                # 初始值
                d = 2*dy - dx
                # 增量
                d1 = 2 * dy
                d2 = 2 * (dy - dx)
                # 从左向右画
                if x1 > xn:
                    x1, xn = xn, x1
                    y1, yn = yn, y1
                    d, d1, d2 = -d, -d1, -d2
                # 直线段起点
                x, y = x1, y1
                glBegin(GL_POINTS)
                while x < xn:
                    glVertex2f(x, y)
                    x = x + 1
                    if d < 0:
                        d = d + d1
                    else:
                        y = y + 1
                        d = d + d2
                glEnd()
            elif -1 <= k < 0:
                pass
            elif k > 1:
                pass
            elif k < -1:
                pass

    # 自己的绘图方法，本程序的关键
    def draw(self):
        # 每次绘制图形之前清除颜色缓冲区和深度缓冲区中的信息
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
        # 黑色
        glColor3f(0, 0, 0)
        # 切换到模型观察矩阵，才能正确绘制图形
        glMatrixMode(GL_MODELVIEW)
        glLoadIdentity()
        glTranslatef(0, 0, 0)

        self.draw_line_Bresenham(0, 0, 500, 300)
        # 交换缓冲区，更新图形
        glutSwapBuffers()

    # 消息主循环
    def mainloop(self):
        glutMainLoop()

if __name__ == '__main__':
    # 创建窗口对象，启动消息主循环
    MyPyOpenGLTest().mainloop()
